//===-- RiscoISelDAGToDAG.cpp - A dag to dag inst selector for Risco ------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an instruction selector for the RISCO target.
//
//===----------------------------------------------------------------------===//

#include "RiscoTargetMachine.h"
#include "llvm/Intrinsics.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;


//===----------------------------------------------------------------------===//
// Instruction Selector Implementation
//===----------------------------------------------------------------------===//

//===--------------------------------------------------------------------===//
/// RiscoDAGToDAGISel - RISCO specific code to select RISCO machine
/// instructions for SelectionDAG operations.
///
namespace {

  class RiscoDAGToDAGISel : public SelectionDAGISel {
    /// Subtarget - Keep a pointer to the Risco Subtarget around so that we can
    /// make the right decision when generating code for different targets.
    const RiscoSubtarget &Subtarget;
    RiscoTargetMachine& TM;

  public:

    explicit RiscoDAGToDAGISel(RiscoTargetMachine &tm)
      : SelectionDAGISel(tm),
        Subtarget(tm.getSubtarget<RiscoSubtarget>()),
        TM(tm)
    { }

    SDNode *Select(SDNode *N);

    // Complex Pattern Selectors.
    bool SelectADDRrr(SDValue N, SDValue &R1, SDValue &R2);
    bool SelectADDRri(SDValue N, SDValue &Base, SDValue &Offset);

    /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
    /// inline asm expressions.
    virtual bool SelectInlineAsmMemoryOperand(const SDValue &Op,
            char ConstraintCode,
            std::vector<SDValue> &OutOps);

    virtual const char *getPassName() const
    {
      return "RISCO DAG->DAG Pattern Instruction Selection";
    }

    // Include the pieces autogenerated from the target description.
    #include "RiscoGenDAGISel.inc"

  private:
    SDNode* getGlobalBaseReg();
  };
} // end anonymous namespace


SDNode* RiscoDAGToDAGISel::getGlobalBaseReg()
{
  unsigned GlobalBaseReg = TM.getInstrInfo()->getGlobalBaseReg(MF);
  return CurDAG->getRegister(GlobalBaseReg, TLI.getPointerTy()).getNode();
}


bool RiscoDAGToDAGISel::
SelectADDRri(SDValue Addr, SDValue &Base, SDValue &Offset)
{
  if (FrameIndexSDNode * FIN = dyn_cast<FrameIndexSDNode > (Addr)) {
    Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
    Offset = CurDAG->getTargetConstant(0, MVT::i32);
    return true;
  }

  if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
          Addr.getOpcode() == ISD::TargetGlobalAddress)
    return false; // direct calls.

  if (Addr.getOpcode() == ISD::ADD) {
    if (ConstantSDNode * CN = dyn_cast<ConstantSDNode > (Addr.getOperand(1))) {
      if (isInt < 11 > (CN->getSExtValue())) {
        if (FrameIndexSDNode * FIN =
                dyn_cast<FrameIndexSDNode > (Addr.getOperand(0))) {
          // Constant offset from frame ref.
          Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
        } else {
          Base = Addr.getOperand(0);
        }
        Offset = CurDAG->getTargetConstant(CN->getZExtValue(), MVT::i32);
        return true;
      }
    }
    if (Addr.getOperand(0).getOpcode() == RIISD::LO) {
      Base = Addr.getOperand(1);
      Offset = Addr.getOperand(0).getOperand(0);
      return true;
    }
    if (Addr.getOperand(1).getOpcode() == RIISD::LO) {
      Base = Addr.getOperand(0);
      Offset = Addr.getOperand(1).getOperand(0);
      return true;
    }
  }
  Base = Addr;
  Offset = CurDAG->getTargetConstant(0, MVT::i32);
  return true;
}


bool RiscoDAGToDAGISel::
SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2)
{
  if (Addr.getOpcode() == ISD::FrameIndex) return false;
  
  if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
          Addr.getOpcode() == ISD::TargetGlobalAddress)
    return false; // direct calls.

  if (Addr.getOpcode() == ISD::ADD) {
    if (ConstantSDNode * CN = dyn_cast<ConstantSDNode > (Addr.getOperand(1)))
      if (isInt < 11 > (CN->getSExtValue()))
        return false; // Let the reg+imm pattern catch this!
    if (Addr.getOperand(0).getOpcode() == RIISD::LO ||
            Addr.getOperand(1).getOpcode() == RIISD::LO)
      return false; // Let the reg+imm pattern catch this!
    R1 = Addr.getOperand(0);
    R2 = Addr.getOperand(1);
    return true;
  }

  R1 = Addr;
  R2 = CurDAG->getRegister(Risco::ZERO, MVT::i32);
  return true;
}


SDNode *RiscoDAGToDAGISel::
Select(SDNode *N) {
  DebugLoc dl = N->getDebugLoc();
  if (N->isMachineOpcode())
    return NULL; // Already selected.

  switch (N->getOpcode()) {
    default: break;
    case RIISD::GLOBAL_BASE_REG:
      return getGlobalBaseReg();
  }

  return SelectCode(N);
}


/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
/// inline asm expressions.

bool
RiscoDAGToDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op,
        char ConstraintCode,
        std::vector<SDValue> &OutOps) {
  SDValue Op0, Op1;
  switch (ConstraintCode) {
    default: return true;
    case 'm': // memory
      if (!SelectADDRrr(Op, Op0, Op1))
        SelectADDRri(Op, Op0, Op1);
      break;
  }

  OutOps.push_back(Op0);
  OutOps.push_back(Op1);
  return false;
}

/// createRiscoISelDag - This pass converts a legalized DAG into a
/// RISCO-specific DAG, ready for instruction scheduling.
///

FunctionPass *llvm::createRiscoISelDag(RiscoTargetMachine &TM) {
  return new RiscoDAGToDAGISel(TM);
}
